require_relative '../ast'

module Y
  class ASTLeaf < Struct.new(:token)
    def num
      0
    end

    def location
      "At Line #{token.lineno} with offset #{token.offset}"
    end

    def to_s
      token.value.to_s
    end
  end

  class ASTList < Struct.new(:children)
    def num
      children.size
    end

    def location 
      t = children.find(&:location)
      # exception handing 
      t.location
    end

    def to_s
      res = children
        .inject('(') { |str, child| str += child.to_s }

      res + ')'
    end

    def self.create(c)
      c.size == 1 ? c.first : ASTList.new(c)
    end
  end

  class Parser

    class Tree
      def initialize(p)
        @p= p
      end

      def parse(lex, res)
        res.push @p.parse(lex)
      end

      def match?(lex)
        @p.match? lex
      end
    end

    class OrTree
      def initialize(ps)
        @ps = ps
      end

      def parse(lex, res)
        p = choose(lex)
        # todo exception handing
        res.push p.parse(lex)
      end

      def match?(lex)
        choose(lex) != nil
      end

      def choose(lex)
        @ps.find { |p| p.match? lex }
      end
    end

    class Repeat
      def initialize(p, o)
        @p    = p
        @once = o
      end

      def parse(lex, res)
        while @p.match?(lex)
          token = @p.parse(lex)
          if token.class != ASTList || 
              token.num > 0
            res.push token
          end

          break if @once
        end
      end

      def match?(lex)
        @p.match? lex
      end
    end


    ### Token ###
    class Token < Struct.new(:type)
      def parse(lex, res)
        token = lex.read
        if test(token)
          res.push type.new(token)
        else
          # exception handing
        end
      end
      
      def match?(lex)
        test lex.peek
      end
    end

    class NumberToken < Token
      def test(token)
        token.number?
      end
    end

    # Identifier Token
    class NameToken < Token
      def initialize(type, r)
        super type
        @reversed = r
      end
        
      def test(token)
        token.identifier? && 
          !@reversed.include?(token.value)
      end
    end

    ### Expr ###
    class Operators
      Prec = Struct.new(:v, :l) do 
        def right?(another)
          if another.l
            v < another.v
          else
            v <= another.v
          end
        end
      end

      RIGHT = false
      LEFT  = true

      def self.add(op, v, l)
        Operators.new.tap do |ops|
          ops.add op, v, l
        end
      end

      def initialize
        @hash = { }
      end

      def [](op)
        @hash[op]
      end

      def add(op, v, l)
        @hash[op] ||= Prec.new(v, l)
        self
      end
    end

    class Expr < Struct.new(:type, :factor, :ops)
      def parse(lex, res)
        right = factor.parse(lex)
        while prec = next_op(lex)
          right = do_shift(lex, right, prec)
        end

        res.push right
      end

      def next_op(lex)
        ops[lex.peek.value]
      end

      def do_shift(lex, left, prec)
        list = [left, ASTLeaf.new(lex.read)]

        right = factor.parse(lex)
        while nxt = next_op(lex)  
          break if !prec.right?(nxt)
          right = do_shift(lex, right, nxt)
        end

        type.new list.push(right)
      end

      def match?(lex)
        factor.match? lex
      end
    end

    class Leaf < Struct.new(:tokens)
      def parse(lex, res)
        token = lex.read
        if token.identifier? && 
            tokens.include?(token.value)
          find res, token
        else
          # exception handing 
        end
      end

      def find(res, token)
        res.push ASTLeaf.new(token)
      end

      def match?(lex)
        token = lex.peek
        token.identifier? && 
          tokens.include?(token.value) ?
          true :
          false
      end
    end

    class Skip < Leaf
      def find(res, token)
      end
    end

    ### Parser ###

    def initialize(type, elem)
      @type = type
      @elem = elem
    end

    def self.rule(type = ASTList)
      Parser.new type, []
    end

    def parse(lex)
      res = []
      @elem.each { |p| p.parse lex, res }
      if @type.respond_to?(:create)
        @type.create res
      else
        @type.new res
      end
    end

    def match?(lex)
      return true if @elem.empty?

      @elem.first.match? lex
    end

    def ast(p)
      @elem.push Tree.new(p)
      self
    end

    def or(*ps)
      @elem.push OrTree.new(ps)
      self
    end

    def maybe(p)
      tp = p.instance_eval { @type }
      p0 = Parser.new(tp, [])

      @elem.push OrTree.new([p, p0])
      self
    end

    def repeat(p)
      @elem.push Repeat.new(p, false)
      self
    end

    def option(p)
      @elem.push Repeat.new(p, true)
      self
    end

    def number(type = ASTLeaf)
      @elem.push NumberToken.new(type)
      self
    end

    def identifier(reversed = [], type = ASTLeaf)
      @elem.push NameToken.new(type, reversed)
      self
    end

    def expression(type, factor, ops)
      @elem.push Expr.new(type, factor, ops)
      self
    end

    def sep(*pat)
      @elem.push Skip.new(pat)
      self
    end
  end
end

